package controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

import networkcontroller.NetworkController;
import gui.GUI;
import model.Model;
import commands.*;
import commands.controller.*;
import commands.gui.*;
import commands.model.*;
import commands.model.ModelCommand.ModelCommandType;
import commands.nc.*;
import commands.nc.NetworkControllerCommand.NetworkControllerCommandType;
import controller.ControllerFile.ControllerFilesState;
import dataTypes.File;
import dataTypes.DownloadFile.DownloadStateType;

/**
 * Main controller module, handle messages from GUI, Model and NetworkController modules.
 * See commands.controller package for detailed messages information.
 * @author Jakub Skierbiszewski
 */
public class Controller implements Runnable
{
	/** Main gui module */
	private GUI gui;
	
	/** Main model module */
    private Model model;
	
	/** Main controller module */
    private NetworkController networkController;
    
    /** Conrtoller input queue for message handling */
    private LinkedBlockingQueue<Command> inputQueue;
    
    
    /** Number of left connections (stored locally, updated by message from NC) */
    private Integer leftConnectionsNumber;
    /** Local file list, store information about file, part number and host.
     * Used for model-networkConrtoller communication.
     */
    private LinkedList<ControllerFile> fileList;
    /** Max connections per file */
    private Integer MaxConnPerFile;
    /** Apllication state, if true - application close requested */
    private boolean isAppExit;
    /** Host state, updated by message from timer, if true, clears and updates 
     * file host lists on fileList got from model
     */
    private boolean isHostsToRefresh;
    /**
     * State used to store information if any file is active and hosts for this file are alive.
     * If false - our app is not sending requests vie multicast.
     */
    private boolean isAnythingToDownload;

    
    /**
     * Application start point
     * @param args
     */
    static public void main(String args[]) 
    {
        new Controller();
    }

    /**
     * Constructor, constructs model, GUI and netwrkController module,
     * makes new threads for each module and starts them.
     */
    private Controller() 
    {
    	isAnythingToDownload = false;
    	isHostsToRefresh = false;
    	MaxConnPerFile = 1;
    	leftConnectionsNumber = 0;
    	isAppExit = false;
    	
    	fileList = new LinkedList<ControllerFile>();
    	inputQueue = new LinkedBlockingQueue<Command>();
    	
    	new HostFinder(this);
    	
    	// Start GUI
        gui = new GUI(this);
        Thread guiThread = new Thread(gui);
        guiThread.start();
      
        // start Model thread
        model = new Model(this);
        Thread modelThread = new Thread(model);
        modelThread.start();
        
        //start networking
        networkController = new NetworkController(this);
        Thread networkControllerThread= new Thread(networkController);
        networkControllerThread.start();
        //networkControllerTest();
        
        startup();
        
        //Start CONTROLLER
        Thread conrtollerThread = new Thread(this);
        conrtollerThread.start();
          
    }

    /** Method sending startup messages to other modules */
    private void startup()
	{
		try
		{
			model.writeMsg(new ModelCommand(ModelCommandType.GET_MAX_ACTIVE_CONNECTIONS_NUMBER));
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		
	}

	/**
	 * @deprecated Use writeMsg() instead. Adds a command to the queue.
	 * @param command a command to be added 
	 */
    public void reportAction(ControllerCommand command)
    {
    	try
		{
			writeMsg(command);
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
    }
    
    /**
	 * Adds a command to the queue.
	 * @param command a command to be added 
	 */
    public void writeMsg(ControllerCommand command) throws InterruptedException
    {
    	inputQueue.put(command);
    }
    
    /** Main controller thread method, handle message input from queue */ 
    public void run() 
	{
    	BufferedReader reader = new BufferedReader(new InputStreamReader(System.in) );
		ControllerCommand cmd;
		String line = null;
		//Main thread loop
		while (true)
		{
			try
			{
				cmd = (ControllerCommand)inputQueue.take();
				System.out.println("Controller: got new command: " + cmd.getCommandType());
				/*System.out.println("///// Waiting for prompt....");
				try
				{
					line = reader.readLine();
					if(line.equals("s"))
					{
						showControllerFileList();
						line = reader.readLine();
					}
				} catch (IOException e)
				{
					e.printStackTrace();
				}*/
				switch (cmd.getCommandType())
				{
					
					case GUI_ADD_DOWNLOAD:
						if(cmd instanceof CC_GUIAddDownload)
							handleGUI_ADD_DOWNLOAD( (CC_GUIAddDownload)cmd );
						break;
					case GUI_ADD_SHARED:
						if(cmd instanceof CC_GUIAddShared)
							handleGUI_ADD_SHARED( (CC_GUIAddShared)cmd );
						break;
					case GUI_DEL_DOWNLOAD:
						if(cmd instanceof CC_GUIDelDownload)
							handleGUI_DEL_DOWNLOAD( (CC_GUIDelDownload)cmd);
						break;
					case GUI_DEL_SHARED:
						if (cmd instanceof CC_GUIDelShared)
							handleGUI_DEL_SHARED( (CC_GUIDelShared)cmd );
						break;
					case GUI_EXIT:
						handleGUI_EXIT();
						break;
					case GUI_GET_ACTIVE_HOSTS:
						handleGUI_GET_ACTIVE_HOSTS();
						break;
					case GUI_MOVE_DOWNLOAD_FILE:
						if (cmd instanceof CC_GUIMoveDownloadTask)
							handleGUI_MOVE_DOWNLOAD_FILE( (CC_GUIMoveDownloadTask)cmd );
						break;
					case GUI_REFRESH_DOWNLOAD_STATUS_ALL:
						handleGUI_REFRESH_DOWNLOAD_STATUS_ALL();
						break;
					case GUI_SEARCH:
						if (cmd instanceof CC_GUISearch)
							handleGUI_SEARCH( (CC_GUISearch)cmd );
						break;
					case GUI_START_DOWNLOAD_FILE:
						if (cmd instanceof CC_GUIStartDownloadFile)
							handleGUI_START_DOWNLOAD_FILE( (CC_GUIStartDownloadFile)cmd );
						break;
					case GUI_STOP_DOWNLOAD_FILE:
						if (cmd instanceof CC_GUIStopDownloadFile)
							handleGUI_STOP_DOWNLOAD_FILE( (CC_GUIStopDownloadFile)cmd );
						break;
					case GUI_GET_PREFERENCES:
						handleGUI_GET_PREFERENCES();
						break;
					case GUI_SET_PREFERENCES:
						if (cmd instanceof CC_GUISetPreferences)
							handleGUI_SET_PREFERENCES( (CC_GUISetPreferences)cmd );
						break;
					//===========MODEL=========
					case MODEL_DOWNLOAD_FILE_HOSTS_LIST:
						if (cmd instanceof CC_Model_DownloadFileHostsList)
							handleMODEL_DOWNLOAD_FILE_HOSTS_LIST( (CC_Model_DownloadFileHostsList)cmd );
						break;
					case MODEL_DOWNLOAD_FILE_STATE_CHANGED:
						if (cmd instanceof CC_Model_DownloadFileStateChanged)
							handleMODEL_DOWNLOAD_FILE_STATE_CHANGED( (CC_Model_DownloadFileStateChanged)cmd );
						break;
					case MODEL_DOWNLOAD_FILES_LIST:
						if (cmd instanceof CC_Model_DownloadFilesList)
							handleMODEL_DOWNLOAD_FILES_LIST( (CC_Model_DownloadFilesList)cmd );
						break;
					case MODEL_DOWNLOAD_FINISHED:
						if (cmd instanceof CC_Model_DownloadFinished)
							handleMODEL_DOWNLOAD_FINISHED( (CC_Model_DownloadFinished)cmd );
						break;
					case MODEL_MAX_ACTIVE_CONNECTIONS_NUMBER:
						if (cmd instanceof CC_Model_MaxActiveConnectionsNumber)
							handleMODEL_MAX_ACTIVE_CONNECTIONS_NUMBER( (CC_Model_MaxActiveConnectionsNumber)cmd );
						break;
					case MODEL_NEXT_HOST:
						if (cmd instanceof CC_Model_NextHost)
							handleMODEL_NEXT_HOST( (CC_Model_NextHost)cmd );
						break;
					case MODEL_NEXT_LACKING_PART:
						if (cmd instanceof CC_Model_NextLackingPart)
							handleMODEL_NEXT_LACKING_PART( (CC_Model_NextLackingPart)cmd );
						break;
					case MODEL_PART_OF_FILE:
						if (cmd instanceof CC_Model_PartOfFile)
							handleMODEL_PART_OF_FILE( (CC_Model_PartOfFile)cmd );
						break;
					case MODEL_SEARCH_RESULTS:
						if (cmd instanceof CC_Model_SearchResults)
							handleMODEL_SEARCH_RESULTS( (CC_Model_SearchResults)cmd );
						break;
					case MODEL_SHARED_FILES_LIST:
						if (cmd instanceof CC_Model_SharedFilesList)
							handleMODEL_SHARED_FILES_LIST( (CC_Model_SharedFilesList)cmd );
						break;
					case MODEL_CONFIGURATION_SAVED:
						handleMODEL_CONFIGURATION_SAVED();
						break;
					case MODEL_IS_SHARED_FILE_RESPONSE:
						if (cmd instanceof CC_Model_IsSharedFileResponse)
							handleMODEL_IS_SHARED_FILE_RESPONSE( (CC_Model_IsSharedFileResponse)cmd );
						break;
						
					//================NETWORK_CONTROLER
					case NC_HOST_ALIVE:
						if (cmd instanceof CC_NC_HostAlive)
							handleNC_HOST_ALIVE( (CC_NC_HostAlive)cmd );
						break;
					case NC_PART_OF_FILE:
						if (cmd instanceof CC_NC_PartOfFile)
							handleNC_PART_OF_FILE( (CC_NC_PartOfFile)cmd );
						break;
					case NC_SEARCH_QUERY:
						if (cmd instanceof CC_NC_SearchQuery)
							handleNC_SEARCH_QUERY( (CC_NC_SearchQuery)cmd );
						break;
					case NC_SEARCH_RESULT:
						if (cmd instanceof CC_NC_SearchResult)
							handleNC_SEARCH_RESULT( (CC_NC_SearchResult)cmd );
						break;
					case NC_GET_SEGMENT:
						if (cmd instanceof CC_NC_GetSegment)
							handleNC_GET_SEGMENT( (CC_NC_GetSegment)cmd );
						break;
					case NC_FREE_CONNECTIONS:
						if (cmd instanceof CC_NC_FreeConnections)
							handleNC_FreeConnections( (CC_NC_FreeConnections)cmd );
						break;
					case NC_PART_TIMEOUT:
						if (cmd instanceof CC_NC_PartTimeout)
							handleNC_PART_TIMEOUT( (CC_NC_PartTimeout)cmd );
						break;
					case CC_GET_FILES_HOSTS:
						handleCC_GET_FILES_HOSTS();
						break;
					default:
				}				
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}

    //////////////////////////////////////////////////
    /// GUI


	private void handleGUI_ADD_DOWNLOAD(CC_GUIAddDownload cmd)
	{
		File file = new File(cmd.getDstPath()+File.separator+cmd.getFilename(),
								cmd.getSize(),cmd.getHash());
		try
		{
			model.writeMsg( new MCAddDownloadFile(file) );
			//Get file host here!!!!
			//networkController.writeMsg( new NCSearch(cmd.getFilename(), (int)cmd.getSize(), (int)cmd.getSize(), cmd.getHash(), Integer.MAX_VALUE) );
			networkController.writeMsg( new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		} 
	}
	
	private void handleGUI_ADD_SHARED(CC_GUIAddShared cmd)
	{
		File file = new File(cmd.getPath()+File.separator+cmd.getFilename(), 0, null);
		try
		{
			model.writeMsg( new MCAddSharedFile(file));
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_DEL_DOWNLOAD(CC_GUIDelDownload cmd)
	{
		try
		{
			model.writeMsg( new MCRemoveDownloadFile(cmd.getFile()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_DEL_SHARED(CC_GUIDelShared cmd)
	{
		File file = new File(cmd.getPath()+File.separator+cmd.getFilename(),0,null);
		try
		{
			model.writeMsg( new MCRemoveSharedFile(file) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_EXIT()
	{
		try
		{
			isAppExit = true;
			model.writeMsg( new ModelCommand(ModelCommandType.SAVE_CONFIGURATION) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_GET_ACTIVE_HOSTS()
	{
		System.out.println("Controller: Recived GUI_GET_ACTIVE_HOSTS");
		try
		{
			networkController.writeMsg( new NCAsk() );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_MOVE_DOWNLOAD_FILE(CC_GUIMoveDownloadTask cmd)
	{
		System.out.println("Controller: Recived GUI_MOVE_DOWNLOAD_FILE");
		try
		{
			model.writeMsg( new MCMoveDownloadFile(cmd.getFile(), cmd.getPriority()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_REFRESH_DOWNLOAD_STATUS_ALL()
	{
		System.out.println("Controller: Recived GUI_REFRESH_DOWNLOAD_STATUS_ALL");
		try
		{
			model.writeMsg(new ModelCommand(ModelCommandType.GET_DOWNLOAD_FILES_LIST) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_SEARCH(CC_GUISearch cmd)
	{
		try
		{
			networkController.writeMsg(new NCSearch(cmd.getName(), cmd.getMinSize(), cmd.getMaxSize(), null, cmd.getIdSearch()));
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_START_DOWNLOAD_FILE(CC_GUIStartDownloadFile cmd)
	{
		try
		{
			model.writeMsg( new MCStartDownloadFile(cmd.getFile()) );
			networkController.writeMsg( new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_STOP_DOWNLOAD_FILE(CC_GUIStopDownloadFile cmd)
	{
		try
		{
			model.writeMsg( new MCStopDownloadFile(cmd.getFile()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleGUI_GET_PREFERENCES()
	{
		try
		{
			model.writeMsg(new ModelCommand(ModelCommandType.GET_MAX_ACTIVE_CONNECTIONS_NUMBER) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	private void handleGUI_SET_PREFERENCES(CC_GUISetPreferences cmd)
	{
		try
		{
			if (cmd.getConnectionsPerDownload() <= cmd.getMaxActiveDownloads())
				MaxConnPerFile = cmd.getConnectionsPerDownload();
			else
				MaxConnPerFile = cmd.getMaxActiveDownloads();
			model.writeMsg(new MCSetMaxActiveConnectionsNumber(cmd.getMaxActiveDownloads(), this.MaxConnPerFile ) );
			model.writeMsg(new ModelCommand(ModelCommandType.GET_MAX_ACTIVE_CONNECTIONS_NUMBER) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}	
	}

	//////////////////////////////////////////////////
	///	MODEL
	
	private void handleMODEL_DOWNLOAD_FILE_HOSTS_LIST(CC_Model_DownloadFileHostsList cmd)
	{
		System.out.println("Controller: Recived MODEL_DOWNLOAD_FILE_HOSTS_LIST");
	}
	
	private void handleMODEL_DOWNLOAD_FILE_STATE_CHANGED(CC_Model_DownloadFileStateChanged cmd)
	{
		System.out.println("Controller: Recived MODEL_DOWNLOAD_FILE_STATE_CHANGED");
		try
		{
			model.writeMsg( new ModelCommand(ModelCommandType.GET_DOWNLOAD_FILES_LIST) );
		} catch (InterruptedException e1)
		{
			e1.printStackTrace();
		}
		if(cmd.getDownloadFile().getDownloadState() == DownloadStateType.WAITING_FOR_HOSTS)
		{
			try
			{
				networkController.writeMsg( 
						new NCSearch(null, (int)cmd.getDownloadFile().getSize(),
									(int)cmd.getDownloadFile().getSize(),
									cmd.getDownloadFile().getHash(),
									Integer.MAX_VALUE) );
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		else if (cmd.getDownloadFile().getDownloadState() == DownloadStateType.ACTIVE)
		{
			isAnythingToDownload = true;
			try
			{
				networkController.writeMsg(new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
	
	private void handleMODEL_DOWNLOAD_FINISHED(CC_Model_DownloadFinished cmd)
	{
		System.out.println("Controller: Recived MODEL_DOWNLOAD_FINISHED");
		System.err.println("File download finished");
	}
	
	private void handleMODEL_MAX_ACTIVE_CONNECTIONS_NUMBER(CC_Model_MaxActiveConnectionsNumber cmd)
	{
		System.out.println("Controller: Recived MODEL_MAX_ACTIVE_CONNECTIONS_NUMBER");
		try
		{
			MaxConnPerFile = cmd.getMaxActiveConnectionsPerDownload();
			gui.writeMsg( new GUICurrentPreferences(cmd.getMaxActiveConnectionsNumber(), MaxConnPerFile) );
			networkController.writeMsg(new NCSetMaxActiveConnectionsNumber(cmd.getMaxActiveConnectionsNumber()) );
			networkController.writeMsg(new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleMODEL_DOWNLOAD_FILES_LIST(CC_Model_DownloadFilesList cmd)
	{
		System.out.println("##Controller: Recived MODEL_DOWNLOAD_FILES_LIST. Start processing...");
		
		//New - refreshing HOST list
		if(isHostsToRefresh)
		{
			System.out.println("Controller: sending ALL FILES host request. Sending all files host DEL to MODEL");
			try
			{
				model.writeMsg(new ModelCommand(ModelCommandType.CLEAR_DOWNLOAD_FILE_HOSTS_LIST));
			} catch (InterruptedException e1)
			{
				e1.printStackTrace();
			}
			File dwnfile;
			for (int i=0; i<cmd.getDownloadFilesList().size(); i++)
			{
				dwnfile = cmd.getDownloadFilesList().get(i);
				try
				{
					networkController.writeMsg( new NCSearch( dwnfile.getPath(), (int)dwnfile.getSize(), (int)dwnfile.getSize(), dwnfile.getHash(), Integer.MAX_VALUE ));
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			isHostsToRefresh = false;
		}
		//Refreshing HOST list END
		
		
		
		int notActive=0, nextFile=0;
		try
		{
			gui.writeMsg(new GUIDownloadStatusAll(cmd.getDownloadFilesList()) );
			System.out.println("Controller: sent new status of donloads to GUI. Got file list of size: " + cmd.getDownloadFilesList().size());
			isAnythingToDownload = false;
			while (leftConnectionsNumber>0 && cmd.getDownloadFilesList().size()!=nextFile)
			{
				if(cmd.getDownloadFilesList().get(nextFile).getDownloadState() != DownloadStateType.ACTIVE)
					notActive++;
				else
				{
					isAnythingToDownload = true;
					model.writeMsg(new MCGetNextLackingPart( (File)cmd.getDownloadFilesList().get(nextFile) ) );
					System.out.println("Controller: requested file part.");
					leftConnectionsNumber--;
				}
				nextFile++;
			}
			if(leftConnectionsNumber == 0)
				isAnythingToDownload = true;
				
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		System.out.println("Controller: DownloadFileList processing DONE");
	}

	private void handleMODEL_NEXT_LACKING_PART(CC_Model_NextLackingPart cmd)
	{
		ControllerFile file = new ControllerFile(cmd.getDownloadFile(), cmd.getNextLackingPart());
		file.setState(ControllerFilesState.WAITING_FOR_HOST);
		if (addFileToDownloadList(file))
		{
			try
			{
				System.out.println("Controller: sending NextHost reqest to model. Requested next lacking part = " + cmd.getNextLackingPart());
				model.writeMsg(new MCGetNextHost( cmd.getDownloadFile() ));
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		else
		{
			try
			{
				networkController.writeMsg( new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		showControllerFileList();
	}

	private void handleMODEL_NEXT_HOST(CC_Model_NextHost cmd)
	{
		System.out.println("##Controller: Recived MODEL_NEXT_HOST");
		ControllerFile file = getControllerFileWaitingForHost(cmd.getDownloadFile());
		if (file != null && file.getState() == ControllerFilesState.WAITING_FOR_HOST)
		{
			System.out.println("Got file from internal list: " + file.getState());
			if (cmd.getNextHost() == null)
			{
				/*try
				{
					System.out.println("Controller: requesting host for file from NC");
					networkController.writeMsg( new NCSearch(null, (int)file.getFile().getSize(), 
							(int)file.getFile().getSize(), 
							file.getFile().getHash(), 
							Integer.MAX_VALUE) );
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}*/
			}
			else
			{
				file.setHost(cmd.getNextHost());
				try
				{
					networkController.writeMsg( new NCGet(file.getHost(), file.getFile(), file.getPartNumber()) );
					System.out.println("NCGET request to NC, file part number: " + file.getPartNumber() + ", host: " + file.getHost());
					file.setState(ControllerFilesState.DOWNLOADING);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
		else
		{
			try
			{
				networkController.writeMsg( new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}

	private void handleMODEL_PART_OF_FILE(CC_Model_PartOfFile cmd)
	{
		System.out.println("Controller: SENDING file part to NETWORK controller...");
		System.out.println("Data sent: " + cmd.getData());
		System.out.println("End of data");
		try
		{
			networkController.writeMsg( new NCSegment(cmd.getFile().getSize(), cmd.getFile().getHash(), cmd.getPartNumber(), cmd.getData() ) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		System.out.println("Controller: SENDING file part to NETWORK controller DONE");
	}
	private void handleMODEL_SEARCH_RESULTS(CC_Model_SearchResults cmd)
	{
		System.out.println("Controller: Returning search results from model to NC");
		try
		{
			networkController.writeMsg( new NCResult(cmd.getResults(), cmd.getSearchID()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleMODEL_SHARED_FILES_LIST(CC_Model_SharedFilesList cmd)
	{
		System.out.println("Controller: returning shared file list to gui");
		try
		{
			gui.writeMsg( new GUISharedFilesList(cmd.getSharedFilesList()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleMODEL_CONFIGURATION_SAVED()
	{
		if (isAppExit)
			System.exit(0);
	}
	
	private void handleMODEL_IS_SHARED_FILE_RESPONSE(CC_Model_IsSharedFileResponse cmd)
	{
		System.out.println("Controller: Returning shared file response from model to NC");
		try
		{
			Map<File, Float> result = new HashMap<File, Float>();
			if(cmd.isShared())
			{
				result.put(cmd.getFile(), 1.0f);
				networkController.writeMsg( new NCResult(result, cmd.getId()) );
			}
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	///////////////////////////////////////////////////////
	/// File list operations
	
	/**
	 * @return false if file and part existed
	 */
	private boolean addFileToDownloadList(ControllerFile file)
	{
		for(int i=0; i<fileList.size(); i++)
		{
			if( fileList.get(i).getFile().equals( file.getFile() )
					&& fileList.get(i).getPartNumber() == file.getPartNumber() )
			{
				System.out.println("File not added to list!! File part = " + file.getPartNumber());
				if(fileList.get(i).getState() == ControllerFilesState.WAITING_FOR_HOST)
					try
					{
						model.writeMsg(new MCGetNextHost( file.getFile() ) );
					} catch (InterruptedException e)
					{
						e.printStackTrace();
					}
				return false;
			}
		}
		fileList.add(file);
		return true;
	}
	
	/**
	 * Return ControllerFile recognized by File descriptor
	 * @param file - file descriptor
	 * @return found ControllerFile or null
	 */
	private ControllerFile getControllerFileWaitingForHost(File file)
	{
		for(int i=0; i<fileList.size(); i++)
		{
			if(fileList.get(i).getFile().equals(file) 
					&& fileList.get(i).getState() == ControllerFilesState.WAITING_FOR_HOST)
				return fileList.get(i);
		}
		return null;
	}

	/**
	 * Removes downloaded file part from list
	 * @param file
	 * @param partNumber
	 */
	private void removeFileFromList(File file, int partNumber)
	{
		for(int i=0; i<fileList.size(); i++)
		{
			if(fileList.get(i).getFile().equals(file)
					&& fileList.get(i).getPartNumber() == partNumber)
			{
				fileList.remove(i);
				System.out.println("Controller: Removed file from internal list: " + partNumber);
				return;
			}
		}		
	}
	private void setAsDownloaded(File file, int partNumber)
	{
		for(int i=0; i<fileList.size(); i++)
		{
			if(fileList.get(i).getFile().equals(file)
					&& fileList.get(i).getPartNumber() == partNumber)
			{
				fileList.get(i).setState(ControllerFilesState.DOWNLOADED);
				return;
			}
		}
	}
	
	private void showControllerFileList()
	{
		for(int i=0; i<fileList.size(); i++)
		{
			System.out.println(i + ". File: size=" + fileList.get(i).getFile().getSize() 
						+ " state: " + fileList.get(i).getState() + " part: " + fileList.get(i).getPartNumber());
		}
	}
	
	///////////////////////////////////////////////////////
	/// Network controller
	
	private void handleNC_HOST_ALIVE(CC_NC_HostAlive cmd)
	{
		System.out.println("Controller: Got HOST from NC");
		try
		{
			gui.writeMsg( new GUIHostActive(cmd.getHost()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleNC_PART_OF_FILE(CC_NC_PartOfFile cmd)
	{
		System.out.println("Controller - got part of file from NC, passing to MODEL");
		try
		{
			model.writeMsg( new MCSavePartOfFile(cmd.getFile(), cmd.getData(), cmd.getPartNumber()) );
			networkController.writeMsg( new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
			removeFileFromList(cmd.getFile(), cmd.getPartNumber() );
			setAsDownloaded(cmd.getFile(), cmd.getPartNumber() );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	private void handleNC_SEARCH_RESULT(CC_NC_SearchResult cmd)
	{
		System.out.println("Controller: at handleNC_SEARCH_RESULT");
		if(cmd.getId() == Integer.MAX_VALUE)
		{
			try
			{
				System.out.println("Controller - passing file host to model");
				for( int i=0; i<cmd.getFiles().size(); i++)
					model.writeMsg(new MCAddDownloadFileHost(cmd.getFiles().get(i), cmd.getHost()) );
				networkController.writeMsg ( new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		else 
		{
			try
			{
				System.out.println(cmd.getFiles().size());
				gui.writeMsg( new GUISearchResult(cmd.getId(),cmd.getFiles(), cmd.getHost()) );
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
	
	private void handleNC_GET_SEGMENT(CC_NC_GetSegment cmd)
	{
		System.out.println("Controller: NC requested part, sending request to MODEL");
		File file = new File(null, cmd.getSize(), cmd.getSum());
		try
		{
			model.writeMsg(new MCGetPartOfFile(file, cmd.getSegment()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleNC_SEARCH_QUERY(CC_NC_SearchQuery cmd)
	{
		System.out.println("Controller: recived search from NC, requesting from model");
		try
		{
			if(/*cmd.getId() == Integer.MAX_VALUE*/ cmd.getSum()!=null && !cmd.getSum().equals("")){
				System.out.println("Controller: sending MCIsSharedFile");
				model.writeMsg( new MCIsSharedFile(new File(null, cmd.getMaxSize(), cmd.getSum()), cmd.getId()) );
			}
			else{
				System.out.println("Controller: sending MCSearch");
				model.writeMsg( new MCSearch(cmd.getSearchName(), cmd.getMinSize(), cmd.getMaxSize(), cmd.getId()) );
			}
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleNC_FreeConnections(CC_NC_FreeConnections cmd)
	{
		
		System.out.println("Controller: Recived free connections number: " + cmd.getNumConn() + ".");
		leftConnectionsNumber = cmd.getNumConn();
		if (leftConnectionsNumber != 0 && isAnythingToDownload)
		{
			try
			{
				System.out.println("Requesting file list from model");
				model.writeMsg(new ModelCommand(ModelCommandType.GET_DOWNLOAD_FILES_LIST) );
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}

	private void handleNC_PART_TIMEOUT(CC_NC_PartTimeout cmd)
	{
		System.out.println("Controller: recived part timeout from NC");
		removeFileFromList(cmd.getFile(), cmd.getPart() );
		try
		{
			networkController.writeMsg( new NetworkControllerCommand(NetworkControllerCommandType.GET_FREE_CONNECTIONS) );
			model.writeMsg( new MCRemoveDownloadFileHost(cmd.getFile(), cmd.getHost()) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private void handleCC_GET_FILES_HOSTS()
	{
		System.out.println("Controller: Got GET_FILES_HOSTS, requesting refresh hosts");
		isHostsToRefresh = true;
		try
		{
			model.writeMsg( new ModelCommand(ModelCommandType.GET_DOWNLOAD_FILES_LIST) );
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
}










